Golang-go slice解析

golang slice到底是值传递还是引用传递?
答案:值传递

下面通过几个例子分析一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
slice := []int{1, 2, 3}
newSlice := slice[0:2] //这个地方是关键
fmt.Printf("before the origin slice address=%p \n", slice)
fmt.Printf("before the new slice address=%p \n", slice)
fmt.Println("before the origin slice data = ", slice)
fmt.Println("before the new slice data = ", newSlice)
newSlice = append(newSlice, 4)
fmt.Println("after append the new slice data = ", newSlice)
// 并不会改变原来数组的值
for _, v := range newSlice {
v += 10
}
fmt.Println("after first change the new slice data = ", newSlice)

// 会改变slice和newSlice的值
for i := range newSlice {
newSlice[i] += 10
}

fmt.Printf("after the origin slice address=%p \n", slice)
fmt.Printf("after the new slice address=%p \n", newSlice)
fmt.Println("after second change the new slice data = ", newSlice)
fmt.Println("after second change the origin slice data = ", slice)

如上代码会有如下输出,可以看到subSlice改变了原来slice的值,那么是不是slice就是指针传递呢?

1
2
3
4
5
6
7
8
9
10
before the origin slice address=0xc000090000
before the new slice address=0xc000090000
before the origin slice data = [1 2 3]
before the new slice data = [1 2]
after append the new slice data = [1 2 4]
after first change the new slice data = [1 2 4]
after the origin slice address=0xc000090000
after the new slice address=0xc000090000
after second change the new slice data = [11 12 14]
after second change the origin slice data = [11 12 14]

请看上述代码的简单修改版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
slice := []int{1, 2, 3}
newSlice := slice[1:] // 相比上一个代码,把slice[0:2]修改了slice[1:]
fmt.Printf("before the origin slice address=%p \n", slice)
fmt.Printf("before the new slice address=%p \n", slice)
fmt.Println("before the origin slice data = ", slice)
fmt.Println("before the new slice data = ", newSlice)
newSlice = append(newSlice, 4)
fmt.Println("after append the new slice data = ", newSlice)
// 并不会改变原来数组的值
for _, v := range newSlice {
v += 10
}
fmt.Println("after first change the new slice data = ", newSlice)

// 会改变slice和newSlice的值
for i := range newSlice {
newSlice[i] += 10
}

fmt.Printf("after the origin slice address=%p \n", slice)
fmt.Printf("after the new slice address=%p \n", newSlice)
fmt.Println("after second change the new slice data = ", newSlice)
fmt.Println("after second change the origin slice data = ", slice)

会发现有如下输出:可以发现,subSlice并未改变slice的值,并且他们的地址也发生了变化。这个问题的本质原因是什么呢?

1
2
3
4
5
6
7
8
9
10
before the origin slice address=0xc00008a000
before the new slice address=0xc00008a000
before the origin slice data = [1 2 3]
before the new slice data = [2 3]
after append the new slice data = [2 3 4]
after first change the new slice data = [2 3 4]
after the origin slice address=0xc00008a000
after the new slice address=0xc00008a040
after second change the new slice data = [12 13 14]
after second change the origin slice data = [1 2 3]

本质原因:第一个代码subSlice在做修改的时候,并未超过slice的容量,所以所有的修改都会反映到slice的底层数组上。
但是第二个代码在做append的时候,已经超过了subSlice的容量,底层数组已经放不下了,所以需要扩容,当扩容的时候,newSlice的地址就与之前的地址不一致了。
所以对他的改变就不会体现到原有slice上,所以slice本质上还是值传递。

参考链接

Subslice 例子
golang slice实践以及底层实现